<?php

/**
 * Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
 * Copyright (c) 2010 Mr. Ynk: http://www.mrynk.nl
 */

/**
 * @author Michel Ypma: michel@mrynk.nl
 */

require_once 'interfaces/iClassBuilder.php';
require_once 'BuilderUtils.php';
require_once 'Syntax.php';


class ClassBuilder implements iClassBuilder
{
    private $schema;
    private $language;
    private $ns;
    private $base_class_ns;
    private $class;
    private $import;
    private $build_base;
    private $s_root;


    public function __construct($schema, $build_base = false)
    {
        $this->schema               = $schema;
        $this->language             = $schema['language'];
        $this->ns                   = $schema['ns'];
        $this->base_class_ns        = $schema['base_class_ns'];
        $this->class                = $schema['class'];
        $this->import               = $schema['import'];
        $this->build_base           = $build_base;

        $this->buildManager();
    }

    private function buildManager()
    {
        if ($this->base_class_ns && !$this->build_base)
        {
            $base = new ClassBuilder($this->schema, true);
            $base->write();
        }

        $this->buildClass();
        $this->buildImport();

        if (!$this->base_class_ns || $this->build_base)
        {
            $this->buildAttributes();
            $this->buildMethods();
        }
        elseif (!$this->base_class_ns || !$this->build_base)
        {
            $s_class = $this->s_root->getChild('class');

            $s_attr = $s_class->newChild('attribute');
            $s_attr->set('attribute', '_explicitType');
            $s_attr->set('type', 'String');
            $s_attr->set('value', $this->ns.'.'.BuilderUtils::className($this->class['name']));

            $s_class->addChild($s_attr, 'explicit_type');
        }
    }

    private function buildClass()
    {
        $this->s_root = new Syntax('package', $this->language);
        $ns = (($this->build_base) ? $this->base_class_ns : $this->ns );
        $this->s_root->set('namespace', $ns );

        $s_class = $this->s_root->newChild('class');
        $s_class->set('class', (($this->build_base) ? 'Base' : '' ).BuilderUtils::className($this->class['name']));
        $s_class->set('path_dots', $ns);
        $s_class->set('path_slashes', str_replace('.', '/', $ns));
        $s_class->set('path_underscores', str_replace('.', '_', $ns));

        if ($this->base_class_ns && !$this->build_base)
        {
            $s_class->set('class_extends', 'Base'.BuilderUtils::className($this->class['name']));
        }

        $this->s_root->addChild($s_class);
    }

    private function buildImport()
    {
        $s_class = $this->s_root->getChild('class');

        if ($this->base_class_ns && !$this->build_base)
        {
            $ns = $this->base_class_ns.'.Base'.$this->class['name'];

            $s_import = $s_class->newChild('import');
            $s_import->set('path_dots', $ns);
            $s_import->set('path_slashes', str_replace('.', '/', $ns));
            $s_import->set('path_underscores', str_replace('.', '_', $ns));

            $s_class->addChild($s_import, 'import');
        }

        if (!$this->base_class_ns || $this->build_base)
        {
            foreach ($this->import as $import)
            {
                $class_name = explode('.', $import);

                if ($this->build_base || end($class_name) != $this->class['name'])
                {
                    $s_import = $s_class->newChild('import');
                    $s_import->set('path_dots', $import);
                    $s_import->set('path_slashes', str_replace('.', '/', $import));
                    $s_import->set('path_underscores', str_replace('.', '_', $import));

                    $s_class->addChild($s_import, 'import');
                }
            }
        }
    }

    private function buildAttributes()
    {
        $s_class = $this->s_root->getChild('class');

        foreach ($this->class->attribute as $attribute)
        {
            $s_attr = $s_class->newChild('attribute', $attribute);
            $s_attr->set('attribute', BuilderUtils::attributeName($attribute['name']));
            $s_attr->set('type', $attribute['type']);

            $s_class->addChild($s_attr);
        }
    }

    private function buildMethods()
    {
        $s_class = $this->s_root->getChild('class');

        foreach ($this->class->attribute as $attribute)
        {
            if ($attribute['getter'] != '!')
            {
                $s_method = $this->buildGetter($s_class->newChild('methods/getter'), $attribute);

                $s_class->addChild($s_method);
            }

            if ($attribute['setter'] != '!')
            {
                $s_method = $this->buildGetter($s_class->newChild('methods/setter'), $attribute);

                $s_class->addChild($s_method);
            }
        }
    }

    private function buildGetter($s_method, $attribute)
    {
        $s_method->set('method', BuilderUtils::methodName($attribute['name']));
        $s_method->set('attribute', BuilderUtils::attributeName($attribute['name']));
        $s_method->set('type', $attribute['type']);

        return $s_method;
    }

    private function buildSetter($s_method, $attribute)
    {
        $s_method->set('method', BuilderUtils::methodName($attribute['name']));
        $s_method->set('attribute', BuilderUtils::attributeName($attribute['name']));
        $s_method->set('type', $attribute['type']);

        return $s_method;
    }

    public function write()
    {
        $contents = BuilderUtils::indent($this->s_root->getContents());

        foreach ($this->language as $language)
        {
            $path = $language['target'];

            if ($this->build_base)
            {
                $path .= str_replace('.', '/', $this->base_class_ns.'/');
                $path .= 'Base';
            }
            else
            {
                $path .= str_replace('.', '/', $this->ns.'/');
            }

            $ext = reset( explode( '-', $language['name'] ) );
            $path .= BuilderUtils::className($this->class['name']).'.'.$ext;

            $force = false;

            if (!$this->base_class_ns || $this->build_base)
            {
                $force = true;
            }

            BuilderUtils::writeFile( $path, $contents[(string) $language['name']], $force );
        }
    }
}
?>